001 /*
002 * Copyright 2005 Stephen McConnell
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.station.server;
020
021 import java.net.URI;
022 import java.net.URL;
023 import java.util.Map;
024 import java.util.Hashtable;
025 import java.util.EventObject;
026 import java.util.EventListener;
027 import java.util.ArrayList;
028 import java.io.File;
029 import java.io.IOException;
030 import java.io.FileInputStream;
031 import java.io.OutputStream;
032 import java.io.FileOutputStream;
033 import java.io.FileNotFoundException;
034
035 import net.dpml.util.Logger;
036 import net.dpml.lang.UnknownKeyException;
037 import net.dpml.lang.DuplicateKeyException;
038
039 import net.dpml.station.ApplicationRegistry;
040 import net.dpml.station.RegistryEvent;
041 import net.dpml.station.RegistryListener;
042 import net.dpml.station.builder.RegistryBuilder;
043 import net.dpml.station.builder.RegistryWriter;
044 import net.dpml.station.info.ApplicationDescriptor;
045 import net.dpml.station.info.RegistryDescriptor;
046 import net.dpml.station.info.RegistryDescriptor.Entry;
047 import net.dpml.station.info.ApplicationRegistryRuntimeException;
048
049 import net.dpml.util.StreamUtils;
050
051 /**
052 * Implements of the application registry within which a set of application profiles
053 * are maintained.
054 *
055 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
056 * @version 1.0.1
057 */
058 public class RemoteApplicationRegistry extends DefaultModel implements ApplicationRegistry
059 {
060 private final URL m_url;
061 private final Map m_map = new Hashtable();
062
063 /**
064 * Creation of a new application registry model.
065 * @param logger the assigned logging channel
066 * @param url storage location
067 * @exception Exception if an error occurs
068 */
069 public RemoteApplicationRegistry( Logger logger, URL url ) throws Exception
070 {
071 super( logger );
072
073 try
074 {
075 RegistryDescriptor registry = loadRegistryDescriptor( url );
076 RegistryDescriptor.Entry[] entries = registry.getEntries();
077 for( int i=0; i<entries.length; i++ )
078 {
079 RegistryDescriptor.Entry entry = entries[i];
080 if( null == entry )
081 {
082 final String error =
083 "Internal error causes by a null registry array entry. Probable "
084 + "cause is an incompatible data source.";
085 throw new ApplicationRegistryRuntimeException( error );
086 }
087 String key = entry.getKey();
088 ApplicationDescriptor descriptor = entry.getApplicationDescriptor();
089 m_map.put( key, descriptor );
090 }
091 m_url = url;
092 }
093 catch( Exception e )
094 {
095 final String error =
096 "Unexpected exception raised while loading: " + url;
097 throw new ApplicationRegistryRuntimeException( error, e );
098 }
099 }
100
101 /**
102 * Return the array of application keys.
103 * @return the application key array
104 */
105 public String[] getKeys()
106 {
107 synchronized( getLock() )
108 {
109 return (String[]) m_map.keySet().toArray( new String[0] );
110 }
111 }
112
113 /**
114 * Return the array of application descriptors.
115 * @return the application descriptor array
116 */
117 Entry[] getEntries()
118 {
119 synchronized( getLock() )
120 {
121 String[] keys = getKeys();
122 ArrayList entries = new ArrayList();
123 //Entry[] entries = new Entry[ keys.length ];
124 for( int i=0; i<keys.length; i++ )
125 {
126 String key = keys[i];
127 ApplicationDescriptor descriptor =
128 (ApplicationDescriptor) m_map.get( key );
129 if( null != descriptor )
130 {
131 Entry entry = new Entry( key, descriptor );
132 entries.add( entry );
133 }
134 }
135 return (Entry[]) entries.toArray( new Entry[0] );
136 }
137 }
138
139 /**
140 * Return the number of application descriptors in the registry.
141 * @return the application descriptor count
142 */
143 public int getApplicationDescriptorCount()
144 {
145 return m_map.size();
146 }
147
148 /**
149 * Add an application descriptor to the registry.
150 * @param key the application key
151 * @param descriptor the application descriptor
152 * @exception DuplicateKeyException if the key is already assigned
153 */
154 public void addApplicationDescriptor( String key, ApplicationDescriptor descriptor )
155 throws DuplicateKeyException
156 {
157 synchronized( getLock() )
158 {
159 if( m_map.containsKey( key ) )
160 {
161 throw new DuplicateKeyException( key );
162 }
163 m_map.put( key, descriptor );
164 getLogger().debug( "added application: " + key );
165 ApplicationDescriptorAddedEvent event =
166 new ApplicationDescriptorAddedEvent( this, descriptor );
167 enqueueEvent( event );
168 }
169 }
170
171 /**
172 * Remove an application descriptor from the registry.
173 * @param key the application key
174 * @exception UnknownKeyException if the key is not recognized
175 */
176 public void removeApplicationDescriptor( String key )
177 throws UnknownKeyException
178 {
179 synchronized( getLock() )
180 {
181 if( !m_map.containsKey( key ) )
182 {
183 throw new UnknownKeyException( key );
184 }
185 ApplicationDescriptor descriptor = (ApplicationDescriptor) m_map.get( key );
186 ApplicationDescriptorRemovedEvent event =
187 new ApplicationDescriptorRemovedEvent( this, descriptor );
188 m_map.remove( key );
189 getLogger().debug( "removed application: " + key );
190 enqueueEvent( event );
191 }
192 }
193
194 /**
195 * Replace an application descriptor within the registry with a supplied descriptor.
196 * @param key the application key
197 * @param descriptor the updated application descriptor
198 * @exception UnknownKeyException if the key is not recognized
199 */
200 public void updateApplicationDescriptor( String key, ApplicationDescriptor descriptor )
201 throws UnknownKeyException
202 {
203 synchronized( getLock() )
204 {
205 m_map.put( key, descriptor );
206 getLogger().debug( "updated application: " + descriptor );
207 ApplicationDescriptorAddedEvent event =
208 new ApplicationDescriptorAddedEvent( this, descriptor );
209 enqueueEvent( event );
210 }
211 }
212
213 /**
214 * Return an array of all profiles in the registry.
215 * @return the application profiles
216 */
217 public ApplicationDescriptor[] getApplicationDescriptors()
218 {
219 synchronized( getLock() )
220 {
221 return (ApplicationDescriptor[]) m_map.values().toArray( new ApplicationDescriptor[0] );
222 }
223 }
224
225 /**
226 * Retrieve an application profile.
227 * @param key the application profile key
228 * @return the application profile
229 * @exception UnknownKeyException if the key is unknown
230 */
231 public ApplicationDescriptor getApplicationDescriptor( String key )
232 throws UnknownKeyException
233 {
234 synchronized( getLock() )
235 {
236 if( !m_map.containsKey( key ) )
237 {
238 throw new UnknownKeyException( key );
239 }
240 return (ApplicationDescriptor) m_map.get( key );
241 }
242 }
243
244 /**
245 * Flush the state of the server to external storage.
246 * @exception IOException if an I/O error occurs
247 */
248 public void flush() throws IOException
249 {
250 synchronized( getLock() )
251 {
252 if( null == m_url )
253 {
254 return;
255 }
256
257 File file = File.createTempFile( "dpml-station", ".xml" );
258 getLogger().debug( "writing to temp file: " + file );
259 file.createNewFile();
260 FileOutputStream output = new FileOutputStream( file );
261 Entry[] entries = getEntries();
262 RegistryDescriptor descriptor = new RegistryDescriptor( entries );
263 RegistryWriter writer = new RegistryWriter();
264 writer.writeRegistryDescriptor( descriptor, output, "" );
265 FileInputStream input = new FileInputStream( file );
266 OutputStream dest = m_url.openConnection().getOutputStream();
267 StreamUtils.copyStream( input, dest, true );
268 getLogger().debug( "updated registry: " + m_url );
269 }
270 }
271
272 /**
273 * Add a depot content change listener.
274 * @param listener the registry change listener to add
275 */
276 public void addRegistryListener( RegistryListener listener )
277 {
278 super.addListener( listener );
279 }
280
281 /**
282 * Add a registry change listener.
283 * @param listener the registry change listener to add
284 */
285 public void removeRegistryListener( RegistryListener listener )
286 {
287 super.removeListener( listener );
288 }
289
290 /**
291 * Proces a registry event.
292 * @param event the event top process
293 */
294 protected void processEvent( EventObject event )
295 {
296 if( event instanceof RegistryEvent )
297 {
298 processRegistryEvent( (RegistryEvent) event );
299 }
300 else
301 {
302 final String error =
303 "Event class not recognized: "
304 + event.getClass().getName();
305 throw new IllegalArgumentException( error );
306 }
307 }
308
309 /**
310 * Return a string representation of the registy model.
311 * @return the string value
312 */
313 public String toString()
314 {
315 return "[registry]";
316 }
317
318 private RegistryDescriptor loadRegistryDescriptor( URL url ) throws Exception
319 {
320 if( null == url )
321 {
322 return new RegistryDescriptor( new Entry[0] );
323 }
324 else
325 {
326 RegistryBuilder builder = new RegistryBuilder();
327 try
328 {
329 Object object = builder.build( new URI( url.toString() ) );
330 if( object instanceof RegistryDescriptor )
331 {
332 return (RegistryDescriptor) object;
333 }
334 else
335 {
336 final String error =
337 "The object returned from the uri ["
338 + url
339 + "] of the class ["
340 + object.getClass().getName()
341 + "] is not an instance of "
342 + RegistryDescriptor.class.getName()
343 + ".";
344 throw new IllegalArgumentException( error );
345 }
346 }
347 catch( FileNotFoundException e )
348 {
349 return new RegistryDescriptor( new Entry[0] );
350 }
351 }
352 }
353
354 private void processRegistryEvent( RegistryEvent event )
355 {
356 EventListener[] listeners = super.listeners();
357 for( int i=0; i<listeners.length; i++ )
358 {
359 EventListener listener = listeners[i];
360 if( listener instanceof RegistryListener )
361 {
362 RegistryListener rl = (RegistryListener) listener;
363 if( event instanceof ApplicationDescriptorAddedEvent )
364 {
365 try
366 {
367 rl.profileAdded( event );
368 }
369 catch( Throwable e )
370 {
371 final String error =
372 "RegistryListener profile addition notification error.";
373 getLogger().error( error, e );
374 }
375 }
376 else if( event instanceof ApplicationDescriptorRemovedEvent )
377 {
378 try
379 {
380 rl.profileRemoved( event );
381 }
382 catch( Throwable e )
383 {
384 final String error =
385 "RegistryListener profile removed notification error.";
386 getLogger().error( error, e );
387 }
388 }
389 }
390 }
391 }
392
393 /**
394 * ApplicationDescriptorAddedEvent.
395 */
396 static class ApplicationDescriptorAddedEvent extends RegistryEvent
397 {
398 /**
399 * Creation of a new ProfileAddedEvent.
400 * @param source the source registry
401 * @param descriptor the application descriptor that was added
402 */
403 public ApplicationDescriptorAddedEvent(
404 ApplicationRegistry source, ApplicationDescriptor descriptor )
405 {
406 super( source, descriptor );
407 }
408 }
409
410 /**
411 * ApplicationDescriptorRemovedEvent.
412 */
413 static class ApplicationDescriptorRemovedEvent extends RegistryEvent
414 {
415 /**
416 * Creation of a new ProfileRemovedEvent.
417 * @param source the source registry
418 * @param descriptor the application descriptor that was removed
419 */
420 public ApplicationDescriptorRemovedEvent( ApplicationRegistry source, ApplicationDescriptor descriptor )
421 {
422 super( source, descriptor );
423 }
424 }
425 }